//@author A0088310L
#include "gtest/gtest.h" //include to use Google Unit test's stuff
#include <string>
#include "Executor.h"
#include "Task.h"

#define TAB		"\t"
/************************************************************************/

/* We write test cases here */

/************************************************************************/

TEST(basic_test, to_lower_case)
{
	Executor execute;
	string actual, expected;

	actual = execute.toLowerCase("INLowErANduPpeR");
	expected = "inlowerandupper";
	ASSERT_EQ(expected, actual);

	actual = execute.toLowerCase("UPPeRwith1093634");
	expected = "upperwith1093634";
	ASSERT_EQ(expected, actual);

	actual = execute.toLowerCase("UPPeRwith!*#&");
	expected = "upperwith!*#&";
	ASSERT_EQ(expected, actual);

}

TEST(basic_test, get_first_word)
{
	Executor execute;
	string actual, expected;

	actual = execute.getFirstWord("proper string");
	expected = "proper";
	ASSERT_EQ(expected, actual);

	actual = execute.getFirstWord("!)@&# and proper string ");
	expected = "!)@&#";
	ASSERT_EQ(expected, actual);

	actual = execute.getFirstWord("");
	expected = "";
	ASSERT_EQ(expected, actual);
}

TEST(basic_test, extract_first_word)
{
	Executor execute;
	string toTest, actual, expected;

	toTest = "proper string";
	actual = execute.extractFirstWord(toTest);
	expected = "proper";
	ASSERT_EQ(expected, actual);
	ASSERT_EQ("string", toTest);

	toTest = "!)@&# and proper string";
	actual = execute.extractFirstWord(toTest);
	expected = "!)@&#";
	ASSERT_EQ(expected, actual);
	ASSERT_EQ("and proper string", toTest);

	toTest = "";
	actual = execute.extractFirstWord(toTest);
	expected = "";
	ASSERT_EQ(expected, actual);
	ASSERT_EQ("", toTest);

	toTest = " with space infront";
	actual = execute.extractFirstWord(toTest);
	expected = "";
	ASSERT_EQ(expected, actual);
	ASSERT_EQ("with space infront", toTest);
}

TEST(basic_test, execute_invalid_command)
{
	string actual, expected;
	Executor execute;
	Task* t;

	t = new Task();
	actual =  execute.executeCommand("modify 9");
	expected = "";

	ASSERT_EQ(expected, actual);
}

TEST(basic_test, basicTaskInfo)
{
	ostringstream expected;
	string actual, expected_str;
	Executor execute;
	Task* t = new Task();
	
	t->appendToName("cs2103 meeting");
	t->setDate("12/12/2012");
	t->appendToDetail("wear formals");	// not shown in basic task info
	t->setImpt(true);					// not shown in basic task info

	execute.addToTaskList(t);
	ASSERT_EQ (1,  execute.taskList.size());	//making sure its added

	actual = execute.basicTaskInfo(0);
	
	//only shows name, date, time (if specified) and task status
	expected << 1 << ". " << TAB << "Name : " << "cs2103 meeting" << "\n";
	expected << TAB << "Date : " << "12/12/2012" << "\n";
	expected << TAB << "Task Status : Incomplete" << "\n";
	
	expected_str = expected.str();

	ASSERT_EQ (expected_str, actual);

	execute.taskList.clear();
}

TEST(basic_test, allTaskInfo)
{
	ostringstream expected;
	string actual, expected_str;
	Executor execute;
	Task* t = new Task();
	
	t->appendToName("cs2103 meeting");
	t->setDate("12/12/2012");
	t->setStartTime(900);
	t->setEndTime(1200);
	t->appendToDetail("wear formals");	
	t->setImpt(true);					

	execute.addToTaskList(t);
	ASSERT_EQ (1,  execute.taskList.size());
	actual = execute.allTaskInfo(0);
	
	//shows all task attributes specified
	expected << 1 << ". " << TAB << "Name : " << "cs2103 meeting" << "\n";
	expected << TAB << "Date : " << "12/12/2012" << "\n";
	expected << TAB << "Start Time : " << 900 << "\n";
	expected << TAB << "End Time : " << 1200 << "\n";
    expected << TAB << "Detail : " << "wear formals" << "\n";
	expected << TAB << "Task Priority : Important" << "\n";
	expected << TAB << "Task Status : Incomplete" << "\n";
	
	expected_str = expected.str();

	ASSERT_EQ (expected_str, actual);

	execute.taskList.clear();
}

TEST(basic_test, execute_add_valid)
{
	Executor execute;
	string actual, expected;
	
	//valid add
	actual = execute.executeAdd("cs2103 meeting -date 26/11");
	expected = execute.basicTaskInfo(0) + "\n\thas been added";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (1,  execute.taskList.size());

	execute.taskList.clear();
}

TEST(basic_test, execute_add_nameNonAlphanumeric)
{
	Executor execute;
	string actual, expected;
	
	//invalid task name
	actual = execute.executeAdd("!@*^ -date 26/11");
	expected = "";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (0,  execute.taskList.size());

	execute.taskList.clear();
}

TEST(basic_test, execute_add_blankAdd)
{
	Executor execute;
	string actual, expected;

	//add without any other input
	actual = execute.executeAdd("");
	expected = execute.basicTaskInfo(0) + "\n\thas been added";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (1,  execute.taskList.size());

	execute.taskList.clear();
}

TEST(basic_test, execute_add_datePastCurrent)
{
	Executor execute;
	string actual, expected;

	//invalid date - date is past current date 
	actual = execute.executeAdd("cs2103 meeting -date 23/02");
	expected = "";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (0,  execute.taskList.size());
	
	execute.taskList.clear();
}

TEST(basic_test, execute_add_dateWithoutMonthAndYear)
{
	Executor execute;
	string actual, expected;
	
	//valid date - date without year and month
	actual = execute.executeAdd("cs2103 meeting -date 28");
	expected = execute.basicTaskInfo(0) + "\n\thas been added";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (1,  execute.taskList.size());

	string addedDate = execute.taskList[0]->getDate();
	string expectedDate = "28/11/2012";
	ASSERT_EQ(expectedDate, addedDate);

	execute.taskList.clear();
}

TEST(basic_test, execute_add_dateBlank)
{
	Executor execute;
	string actual, expected;

	//blank date
	actual = execute.executeAdd("cs2103 meeting -date");
	expected = "";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (0,  execute.taskList.size());

	execute.taskList.clear();
}

TEST(basic_test, execute_add_dateNotInt)
{
	Executor execute;
	string actual, expected;

	//date not integer
	actual = execute.executeAdd("cs2103 meeting -date 19/dec");
	expected = "";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (0,  execute.taskList.size());

	execute.taskList.clear();
}

TEST(basic_test, execute_add_dateInvalidRange)
{
	Executor execute;
	string actual, expected;

	//invalid date range
	actual = execute.executeAdd("cs2103 meeting -date 19/42/202998");
	expected = "";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (0,  execute.taskList.size());

	execute.taskList.clear();
}

//the four tests below can be done with -ends also

//NOTE : This function might assert due to current date being preset for testing
//would differ according to the date of testing
TEST(basic_test, execute_add_startTimeWithoutDate)			
{
	Executor execute;
	string actual, expected;

	//timing stated without date - program adds current date
	actual = execute.executeAdd("cs2103 meeting -starts 2350");
	expected = execute.basicTaskInfo(0) + "\n\thas been added";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (1,  execute.taskList.size());

	string addedDate =  execute.taskList[0]->getDate();
	string expectedDate = "12/11/2012";					// this date is to be set to the current date 

	ASSERT_EQ(expectedDate, addedDate);

	execute.taskList.clear();
}

TEST(basic_test, execute_add_startTimeHourOnly)
{
	Executor execute;
	string actual, expected;

	//timing with only hr - program adds mins
	actual = execute.executeAdd("cs2103 meeting -date 24/12 -starts 8");
	expected = execute.basicTaskInfo(0) + "\n\thas been added";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (1,  execute.taskList.size());
	
	int addedTime =  execute.taskList[0]->getStartTime();
	int expectedTime = 800;

	ASSERT_EQ(expectedTime, addedTime);

	execute.taskList.clear();
}

TEST(basic_test, execute_add_startTimeOutOfRange)
{
	Executor execute;
	string actual, expected;

	//timing out of range
	actual = execute.executeAdd("cs2103 meeting -date 24/12 -starts 2390");
	expected = "";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (0,  execute.taskList.size());

	execute.taskList.clear();
}

TEST(basic_test, execute_add_startTimeOnly)
{
	Executor execute;
	string actual, expected;

	//only start time entered - program sets end time to be one hr past start time
	actual = execute.executeAdd("cs2103 meeting -date 24/12 -starts 8");
	expected = execute.basicTaskInfo(0) + "\n\thas been added";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (1,  execute.taskList.size());
	
	int addedEndTime =  execute.taskList[0]->getEndTime();
	int expectedEndTime = 900;

	ASSERT_EQ(expectedEndTime, addedEndTime);

	execute.taskList.clear();
}

TEST(basic_test, execute_add_startTimePastEndTime)
{
	Executor execute;
	string actual, expected;

	//start timing past end timing
	actual = execute.executeAdd("cs2103 meeting -date 24/12 -starts 2200 -ends 2100");
	expected = "";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (0,  execute.taskList.size());

	execute.taskList.clear();
}

TEST(basic_test, execute_add_impt)
{
	Executor execute;
	string actual, expected;

	//mark task as impt
	actual = execute.executeAdd("cs2103 meeting -impt");
	expected = execute.basicTaskInfo(0) + "\n\thas been added";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (1,  execute.taskList.size());

	bool actual_impt =  execute.taskList[0]->getImpt();
	bool expected_impt = true;

	ASSERT_EQ(expected_impt, actual_impt);

	execute.taskList.clear();
}

TEST(basic_test, execute_add_detail)
{
	Executor execute;
	string actual, expected;

	//adding detail to task
	actual = execute.executeAdd("cs2103 meeting -detail wear formals");
	expected = execute.basicTaskInfo(0) + "\n\thas been added";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (1,  execute.taskList.size());

	string addedDetail =  execute.taskList[0]->getDetail();
	string expectedDetail = "wear formals";

	ASSERT_EQ(expectedDetail, addedDetail);

	execute.taskList.clear();
}

TEST(basic_test, execute_add_detail_NoRestriction)
{
	Executor execute;
	string actual, expected;

	//unlike add, even a single ! can be added under detail
	actual = execute.executeAdd("cs2103 meeting -detail !");
	expected = execute.basicTaskInfo(0) + "\n\thas been added";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (1,  execute.taskList.size());

	string addedDetail =  execute.taskList[0]->getDetail();
	string expectedDetail = "!";

	ASSERT_EQ(expectedDetail, addedDetail);

	execute.taskList.clear();
}

TEST(basic_test, execute_add_repeatWeekly)
{
	Executor execute;
	string actual, expected;

	//repeat weekly
	actual = execute.executeAdd("cs2103 meeting -date 1/1/2013 -repeat weekly");
	expected = execute.basicTaskInfo(0) + "\n" + execute.basicTaskInfo(1) + "\n" +
		execute.basicTaskInfo(2) + "\n" + execute.basicTaskInfo(3) + "\n" +
		execute.basicTaskInfo(4) + "\n\thas been added";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (5,  execute.taskList.size());

	string dateOfFirstRepeat =  execute.taskList[0]->getDate();
	string expectedFirstDate = "08/01/2013";

	ASSERT_EQ(expectedFirstDate, dateOfFirstRepeat);

	string dateOfFourthRepeat =  execute.taskList[3]->getDate();
	string expectedFourthDate = "29/01/2013";

	ASSERT_EQ(expectedFourthDate, dateOfFourthRepeat);

	execute.taskList.clear();
}

TEST(basic_test, execute_add_repeatMonthly)
{
	Executor execute;
	string actual, expected;

	//repeat monthly
	actual = execute.executeAdd("cs2103 meeting -date 1/8/2013 -repeat monthly");
	expected = execute.basicTaskInfo(0) + "\n" + execute.basicTaskInfo(1) + "\n" +
		execute.basicTaskInfo(2) + "\n" + execute.basicTaskInfo(3) + "\n" +
		execute.basicTaskInfo(4) + "\n\thas been added";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (5,  execute.taskList.size());

	string dateOfFirstRepeat =  execute.taskList[0]->getDate();
	string expectedFirstDate = "01/09/2013";

	ASSERT_EQ(expectedFirstDate, dateOfFirstRepeat);

	string dateOfFouthRepeat =  execute.taskList[3]->getDate();
	string expectedFourthDate = "01/12/2013";

	ASSERT_EQ(expectedFourthDate, dateOfFouthRepeat);

	execute.taskList.clear();
}

TEST(basic_test, execute_add_repeatInvalidFreq)
{
	Executor execute;
	string actual, expected;

	//only recognised keywords for repeat are weekly and monthly
	actual = execute.executeAdd("cs2103 meeting -date 1/1/2013 -repeat by week");
	expected = "";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (0,  execute.taskList.size());

	execute.taskList.clear();
}

TEST(basic_test, execute_delete)
{
	Executor execute;
	string taskInfo, actual, expected;

	//1. add task and ensure its added 
	actual = execute.executeAdd("cs2103 meeting -date 1/1/2013");
	taskInfo = execute.basicTaskInfo(0);
	expected = taskInfo + "\n\thas been added";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (1,  execute.taskList.size());

	//2. now test delete
	actual = execute.executeDelete("1");
	expected = taskInfo + "\n\thas been deleted";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (0,  execute.taskList.size());

	execute.taskList.clear();
}

TEST(basic_test, execute_deleteInvalidRange)
{
	Executor execute;
	string taskInfo, actual, expected;

	//1. add task and ensure its added 
	actual = execute.executeAdd("cs2103 meeting -date 1/1/2013");
	taskInfo = execute.basicTaskInfo(0);
	expected = taskInfo + "\n\thas been added";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (1,  execute.taskList.size());

	//2. now test delete
	actual = execute.executeDelete("13");
	expected = "";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (1,  execute.taskList.size());

	execute.taskList.clear();
}

TEST(basic_test, execute_deleteNegativeInt)
{
	Executor execute;
	string taskInfo, actual, expected;

	//1. add task and ensure its added 
	actual = execute.executeAdd("cs2103 meeting -date 1/1/2013");
	taskInfo = execute.basicTaskInfo(0);
	expected = taskInfo + "\n\thas been added";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (1,  execute.taskList.size());

	//2. now test delete
	actual = execute.executeDelete("-7");
	expected = "";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (1,  execute.taskList.size());

	execute.taskList.clear();
}

//rest of the shows can be done in a similar way. 
TEST(basic_test, execute_showImpt)
{
	Executor execute;
	string taskInfo, actual, expected;

	//1. add tasks and ensure its added 
	actual = execute.executeAdd("cs2103 meeting -impt");
	taskInfo = execute.basicTaskInfo(0);
	expected = taskInfo + "\n\thas been added";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (1,  execute.taskList.size());

	actual = execute.executeAdd("lunch");
	taskInfo = execute.basicTaskInfo(1);
	expected = taskInfo + "\n\thas been added";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (2,  execute.taskList.size());

	actual = execute.executeAdd("dinner -impt");
	taskInfo = execute.basicTaskInfo(2);
	expected = taskInfo + "\n\thas been added";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (3,  execute.taskList.size());

	//2. now test show impt
	actual = execute.executeShow("impt");
	expected = execute.basicTaskInfo(0) + "\n" + execute.basicTaskInfo(2) + "\n";

	ASSERT_EQ(expected, actual);

	execute.taskList.clear();
}

//showing taskInfo on time is also done in the same way.
TEST(basic_test, execute_showTaskDetail)
{
	Executor execute;
	string taskInfo, actual, expected;

	//1. add tasks and ensure its added 
	actual = execute.executeAdd("cs2103 meeting -detail wear formals");
	taskInfo = execute.basicTaskInfo(0);
	expected = taskInfo + "\n\thas been added";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (1,  execute.taskList.size());

	//2. now test show detail of a specific task
	actual = execute.executeShow("detail 1");
	expected = execute.allTaskInfo(0);

	ASSERT_EQ(expected, actual);

	execute.taskList.clear();
}

TEST(basic_test, execute_searchEntireWord)
{
	Executor execute;
	string taskInfo, actual, expected;

	//1. add tasks and ensure its added 
	actual = execute.executeAdd("cs2103 meeting -detail wear formals");
	taskInfo = execute.basicTaskInfo(0);
	expected = taskInfo + "\n\thas been added";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (1,  execute.taskList.size());

	//2. now test search to match with entire task name
	actual = execute.executeSearch("cs2103 meeting");
	expected = execute.basicTaskInfo(0) + "\n";

	ASSERT_EQ(expected, actual);

	execute.taskList.clear();
}

TEST(basic_test, execute_searchPartOfWord)
{
	Executor execute;
	string taskInfo, actual, expected;

	//1. add tasks and ensure its added 
	actual = execute.executeAdd("cs2103 meeting -detail wear formals");
	taskInfo = execute.basicTaskInfo(0);
	expected = taskInfo + "\n\thas been added";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (1,  execute.taskList.size());

	//2. now test search to match with part of word
	actual = execute.executeSearch("cs");
	expected = execute.basicTaskInfo(0) + "\n";

	ASSERT_EQ(expected, actual);

	execute.taskList.clear();
}

TEST(basic_test, execute_clear)
{
	Executor execute;
	string taskInfo, actual, expected;

	//1. add tasks and ensure its added 
	actual = execute.executeAdd("cs2103 meeting");
	taskInfo = execute.basicTaskInfo(0);
	expected = taskInfo + "\n\thas been added";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (1,  execute.taskList.size());

	actual = execute.executeAdd("lunch");
	taskInfo = execute.basicTaskInfo(1);
	expected = taskInfo + "\n\thas been added";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (2,  execute.taskList.size());

	actual = execute.executeAdd("dinner");
	taskInfo = execute.basicTaskInfo(2);
	expected = taskInfo + "\n\thas been added";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (3,  execute.taskList.size());

	//2. now test clear
	actual = execute.executeClear("");
	expected = "All tasks are cleared";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (0,  execute.taskList.size());

	execute.taskList.clear();
}

TEST(basic_test, execute_done_undone)
{
	Executor execute;
	string taskInfo, actual, expected;
	bool addedTaskResult, expectedResult;

	//1. add tasks and ensure its added
	actual = execute.executeAdd("cs2103 meeting");
	taskInfo = execute.basicTaskInfo(0);
	expected = taskInfo + "\n\thas been added";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (1,  execute.taskList.size());

	//2. now test done
	taskInfo = execute.basicTaskInfo(0);
	actual = execute.executeDone("1");
	expected = taskInfo + "\n\thas been marked as done";

	addedTaskResult = execute.taskList[0]->getDone();
	expectedResult = true;

	ASSERT_EQ(expected, actual);
	ASSERT_EQ(expectedResult, addedTaskResult);

	//3. test undone
	taskInfo = execute.basicTaskInfo(0);
	actual = execute.executeUndone("1");
	expected = taskInfo + "\n\thas been marked as undone";

	addedTaskResult = execute.taskList[0]->getDone();
	expectedResult = false;

	ASSERT_EQ(expected, actual);
	ASSERT_EQ(expectedResult, addedTaskResult);

	execute.taskList.clear();
}

//most of the edit functions make use of the makeTask functions already tested by add

TEST(basic_test, execute_postpone)
{
	Executor execute;
	string taskInfo, actual, expected;

	//1. add tasks and ensure its added 
	actual = execute.executeAdd("cs2103 meeting -date 24/12");
	taskInfo = execute.basicTaskInfo(0);
	expected = taskInfo + "\n\thas been added";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (1,  execute.taskList.size());

	//2. now test postpone
	taskInfo = execute.basicTaskInfo(0);
	actual = execute.executePostpone("1 30/12");
	expected = taskInfo + "\n\thas been postponed to 30/12/2012" + "\n";
	
	string addedDate = execute.taskList[0]->getDate();
	string expectedDate = "30/12/2012";

	ASSERT_EQ(expected, actual);
	ASSERT_EQ (expectedDate,  addedDate);

	execute.taskList.clear();
}

void runTest(int argument_count, char** argument_vars)
{

testing::InitGoogleTest(&argument_count, argument_vars); //initialize GTest

RUN_ALL_TESTS();

std::getchar(); //pause the program after all the testing

}